﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Simpletron.Events;

namespace Simpletron
{
  public class ExecutionEngien
  {
    private ExecutionContext context;
    private InstructionMap instructionMap;

    public ExecutionEngien()
    {
      context = new ExecutionContext(); 
      instructionMap = InstructionMap.Instance;     
    }

    public void Start()
    {
      context.ExecutionState = ExecutionState.Started;

      while(context.ExecutionState == ExecutionState.Started) 
      {
        context.InstructionRegister = context.Memory[context.Counter];
        context.OperationCode = context.InstructionRegister / 10000000;
        context.Operand = context.InstructionRegister % 10000000;
        context.Counter++;

        instructionMap[context.OperationCode].Execute(context);
      }
    }

    public void Load(SimpleApp application)
    {

      OnBeginLoading(application, 0);
      
      for (int i = 0; i < application.Size; i++)
      {
        var percent = (i == application.Size - 1) ? 100.0 : (100.0 / application.Size) * (i + 1);

        OnLoading(application, percent); 
        context.Memory[i] = application[i];
        Thread.Sleep(25);
      }

      OnEndLoading(application, 100);
    }

    public void Dump(IMemoryDumpWriter writer)
    {
      writer.WriteDump("\nMemory Dump");
      context.Dump(writer);
      writer.Flush();
      writer.Close();
    }

    public event Action<IOutput> RaiseOutputAwaitsEvent
    {
      add { context.RaiseOutputAwaitsDelegate += value; }
      remove { context.RaiseOutputAwaitsDelegate -= value; }
    }

    public event Action<IInput> RaiseInputRequestEvent
    {
      add { context.RaiseInputRequestDelegate += value; }
      remove { context.RaiseInputRequestDelegate -= value; }
    }

    public event Action<LoadingEventArg> BeginLoadingEvent;

    public event Action<LoadingEventArg> LoadingEvent;

    public event Action<LoadingEventArg> EndLoadingEvent;

    private void OnBeginLoading(SimpleApp application, double percent)
    {
      if (BeginLoadingEvent != null)
      {
        BeginLoadingEvent(new LoadingEventArg(application, percent));
      }
    }

    private void OnLoading(SimpleApp application, double percent)
    {
      if (LoadingEvent != null)
      {
        LoadingEvent(new LoadingEventArg(application, percent));
      }
    }

    private void OnEndLoading(SimpleApp application, double percent)
    {
      if (EndLoadingEvent != null)
      {
        EndLoadingEvent(new LoadingEventArg(application, percent));
      }
    } 
  }
}
